package org.mikhaylov.cp.service.hibernate;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Restrictions;
import org.mikhaylov.cp.service.db.entities.City;
import org.mikhaylov.cp.service.db.entities.Entity;
import org.mikhaylov.cp.service.db.entities.Inventory;
import org.mikhaylov.cp.service.db.entities.InventoryType;
import org.mikhaylov.cp.service.db.entities.Region;
import org.mikhaylov.cp.service.db.entities.User;

public class HibernateManager {

	private static HibernateManager instance;

	private HibernateManager() {
	}

	public static HibernateManager getInstance() {
		if (instance == null) {
			synchronized (HibernateManager.class) {
				if (instance == null) {
					instance = new HibernateManager();
				}
			}
		}
		return instance;
	}

	private Session openSession() {
		return HibernateUtil.getSessionFactory().openSession();
	}

	private void closeSession(Session session) {
		session.flush();
		session.close();
	}

	public User getUser(long id) {
		Session session = openSession();
		Transaction t = session.beginTransaction();

		User user = (User) session.createCriteria(User.class)
				.add(Restrictions.eq("id", id)).uniqueResult();

		t.commit();
		closeSession(session);
		return user;
	}

	public User getUser(String login) {
		Session session = openSession();
		Transaction t = session.beginTransaction();

		User user = (User) session.createCriteria(User.class)
				.add(Restrictions.eq("login", login)).uniqueResult();

		t.commit();
		closeSession(session);
		return user;
	}

	public User getUser(String login, String password) {
		User user = getUser(login);
		if (user == null) {
			return null;
		}
		
		if (user.getPassword().equals(password)) {
			return user;
		}

		return null;
	}

	public InventoryType getInventoryType(Long id) {
		Session session = openSession();
		Transaction t = session.beginTransaction();

		InventoryType inventoryType = (InventoryType) session
				.createCriteria(InventoryType.class)
				.add(Restrictions.eq("id", id)).uniqueResult();

		t.commit();
		closeSession(session);
		return inventoryType;
	}

	public Region getRegion(Long id) {
		Session session = openSession();
		Transaction t = session.beginTransaction();

		Region region = (Region) session.createCriteria(Region.class)
				.add(Restrictions.eq("id", id)).uniqueResult();

		t.commit();
		closeSession(session);
		return region;
	}

	public City getCity(Long id) {
		Session session = openSession();
		Transaction t = session.beginTransaction();

		City city = (City) session.createCriteria(City.class)
				.add(Restrictions.eq("id", id)).uniqueResult();

		t.commit();
		closeSession(session);
		return city;
	}
	
	public City getCity(String name) {
		Session session = openSession();
		Transaction t = session.beginTransaction();

		City city = (City) session.createCriteria(City.class)
				.add(Restrictions.eq("name", name)).uniqueResult();

		t.commit();
		closeSession(session);
		return city;
	}

	public List<Inventory> getInventories(User user) {
		Session session = openSession();
		Transaction t = session.beginTransaction();
		
		Criteria criteria = session.createCriteria(Inventory.class, "inventory");
		criteria.add(Restrictions.eq("user.id", user.getId()));
		
		List<Inventory> result = criteria.list();
		t.commit();
		closeSession(session);
		return result;
	}
	
	public List<Inventory> getInventories(InventoryType inventoryType,
			Double minPrice, Double maxPrice, Region region, City city) {
		Session session = openSession();
		Transaction t = session.beginTransaction();

		Criteria criteria = session.createCriteria(Inventory.class, "inventory");
		if (inventoryType != null) {
			criteria.add(Restrictions.eq("inventoryType.id",
					inventoryType.getId()));
		}
		if (minPrice != null) {
			criteria.add(Restrictions.gt("price", minPrice));
		}
		if (maxPrice != null) {
			criteria.add(Restrictions.lt("price", maxPrice));
		}
		if (region != null) {
			if (city != null) {
				criteria.createAlias("inventory.user", "user");
				criteria.createAlias("user.city", "city");
				criteria.add(Restrictions.eq("city.id", city.getId()));
			}
			else {
				criteria.createAlias("inventory.user", "user");
				criteria.createAlias("user.city", "city");
				criteria.createAlias("city.region", "region");
				criteria.add(Restrictions.eq("region.id", region.getId()));
			}
		}
		
		List<Inventory> result = criteria.list();
		t.commit();
		closeSession(session);
		return result;
	}

	public List<Region> getRegions() {
		Session session = openSession();
		Transaction t = session.beginTransaction();
		
		Criteria c = session.createCriteria(Region.class);
		List<Region> result = c.list();
		
		t.commit();
		closeSession(session);
		
		return result;
	}
	
	public List<City> getCities() {
		Session session = openSession();
		Transaction t = session.beginTransaction();
		
		Criteria c = session.createCriteria(City.class);
		List<City> result = c.list();
		
		t.commit();
		closeSession(session);
		
		return result;
	}
	
	public boolean addUser(User user) {
		if (getUser(user.getLogin()) != null) {
			return false;
		}
		addEntity(user);
		return true;
	}

	public void addEntity(Entity entity) {
		Session session = openSession();
		Transaction t = session.beginTransaction();

		session.save(entity);

		t.commit();
		closeSession(session);
	}
	
	public void updateEntity(Entity entity) {
		Session session = openSession();
		Transaction t = session.beginTransaction();

		session.update(entity);

		t.commit();
		closeSession(session);
	}

	public void updateUserBalance(User user, Double value) {
		Session session = openSession();
		Transaction t = session.beginTransaction();
		
		user.setBalance(user.getBalance() + value);
		session.update(user);
		
		t.commit();
		closeSession(session);
	}
	
}
